{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "30165d30",
   "metadata": {},
   "source": [
    "<a href=\"https://colab.research.google.com/github/run-llama/llama_index/blob/main/docs/examples/retrievers/recurisve_retriever_nodes_braintrust.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "025f3e20-aec9-491c-8c90-234aed406a25",
   "metadata": {},
   "source": [
    "# Recursive Retriever + Node References + Braintrust\n",
    "\n",
    "This guide shows how you can use recursive retrieval to traverse node relationships and fetch nodes based on \"references\".\n",
    "\n",
    "Node references are a powerful concept. When you first perform retrieval, you may want to retrieve the reference as opposed to the raw text. You can have multiple references point to the same node.\n",
    "\n",
    "In this guide we explore some different usages of node references:\n",
    "- **Chunk references**: Different chunk sizes referring to a bigger chunk\n",
    "- **Metadata references**: Summaries + Generated Questions referring to a bigger chunk\n",
    "\n",
    "We evaluate how well our recursive retrieval + node reference methods work using [Braintrust](https://www.braintrustdata.com/). Braintrust is the enterprise-grade stack for building AI products. From evaluations, to prompt playground, to data management, we take uncertainty and tedium out of incorporating AI into your business.\n",
    "\n",
    "You can see example evaluation dashboards here for the:\n",
    "- [base retriever](https://www.braintrustdata.com/app/braintrustdata.com/p/llamaindex-recurisve-retrievers/baseRetriever)\n",
    "- [recursive metadata retreiver](https://www.braintrustdata.com/app/braintrustdata.com/p/llamaindex-recurisve-retrievers/recursiveMetadataRetriever)\n",
    "- [recursive chunk retriever](https://www.braintrustdata.com/app/braintrustdata.com/p/llamaindex-recurisve-retrievers/recursiveChunkRetriever)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c98b1160",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install llama-index-llms-openai\n",
    "%pip install llama-index-readers-file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee583e89-a508-493e-b232-42e520ce19de",
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "# NOTE: Replace YOUR_OPENAI_API_KEY with your OpenAI API Key and YOUR_BRAINTRUST_API_KEY with your BrainTrust API key. Do not put it in quotes.\n",
    "# Signup for Braintrust at https://braintrustdata.com/ and get your API key at https://www.braintrustdata.com/app/braintrustdata.com/settings/api-keys\n",
    "# NOTE: Replace YOUR_OPENAI_KEY with your OpenAI API Key and YOUR_BRAINTRUST_API_KEY with your BrainTrust API key. Do not put it in quotes.\n",
    "%env OPENAI_API_KEY=\n",
    "%env BRAINTRUST_API_KEY=\n",
    "%env TOKENIZERS_PARALLELISM=true # This is needed to avoid a warning message from Chroma"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98ed809c",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install -U llama_hub llama_index braintrust autoevals pypdf pillow transformers torch torchvision"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "273f38de-e79a-4ce2-ad4e-2c70afc33f34",
   "metadata": {},
   "source": [
    "## Load Data + Setup\n",
    "\n",
    "In this section we download the Llama 2 paper and create an initial set of nodes (chunk size 1024)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1eb829ef-b54b-4095-a832-6d1d115aa645",
   "metadata": {},
   "outputs": [],
   "source": [
    "!mkdir data\n",
    "!wget --user-agent \"Mozilla\" \"https://arxiv.org/pdf/2307.09288.pdf\" -O \"data/llama2.pdf\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cd97455-5ff3-43ee-8222-f496ec234dc7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "from llama_index.readers.file import PDFReader\n",
    "from llama_index.core.response.notebook_utils import display_source_node\n",
    "from llama_index.core.retrievers import RecursiveRetriever\n",
    "from llama_index.core.query_engine import RetrieverQueryEngine\n",
    "from llama_index.core import VectorStoreIndex\n",
    "from llama_index.llms.openai import OpenAI\n",
    "import json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a07c0e42-1ae8-4267-9355-6bb75323f82a",
   "metadata": {},
   "outputs": [],
   "source": [
    "loader = PDFReader()\n",
    "docs0 = loader.load_data(file=Path(\"./data/llama2.pdf\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "493e5492-a6ae-4e3e-aa23-274c0605b165",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core import Document\n",
    "\n",
    "doc_text = \"\\n\\n\".join([d.get_content() for d in docs0])\n",
    "docs = [Document(text=doc_text)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c2abcd3-6cae-49dd-8719-9b738d000652",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core.node_parser import SentenceSplitter\n",
    "from llama_index.core.schema import IndexNode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91b997ae-9260-4ae7-af2f-0f8d38625d32",
   "metadata": {},
   "outputs": [],
   "source": [
    "node_parser = SentenceSplitter(chunk_size=1024)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0cda44b0-fd27-4255-9aa7-08d358635772",
   "metadata": {},
   "outputs": [],
   "source": [
    "base_nodes = node_parser.get_nodes_from_documents(docs)\n",
    "# set node ids to be a constant\n",
    "for idx, node in enumerate(base_nodes):\n",
    "    node.id_ = f\"node-{idx}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38e47623-b67d-45d6-9b24-33ba84719f1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core.embeddings import resolve_embed_model\n",
    "\n",
    "embed_model = resolve_embed_model(\"local:BAAI/bge-small-en\")\n",
    "llm = OpenAI(model=\"gpt-3.5-turbo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f43ebab2-fc46-41ea-8a92-9148994d793f",
   "metadata": {},
   "source": [
    "## Baseline Retriever\n",
    "\n",
    "Define a baseline retriever that simply fetches the top-k raw text nodes by embedding similarity."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "704fb3da-710e-4ad9-b630-565911917f0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "base_index = VectorStoreIndex(base_nodes, embed_model=embed_model)\n",
    "base_retriever = base_index.as_retriever(similarity_top_k=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "160c339b-601a-486b-9e17-dd6cc9f133ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "retrievals = base_retriever.retrieve(\n",
    "    \"Can you tell me about the key concepts for safety finetuning\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "632610f3-c8f2-440a-ab27-5ca7d65f882a",
   "metadata": {},
   "outputs": [],
   "source": [
    "for n in retrievals:\n",
    "    display_source_node(n, source_length=1500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96dd8a01-1cae-4614-beab-5b5e0434fefe",
   "metadata": {},
   "outputs": [],
   "source": [
    "query_engine_base = RetrieverQueryEngine.from_args(base_retriever, llm=llm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82ae66ff-7d12-45c8-9b1a-adb20bd3c7ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "response = query_engine_base.query(\n",
    "    \"Can you tell me about the key concepts for safety finetuning\"\n",
    ")\n",
    "print(str(response))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5431df3-d255-4492-bce4-bbebde6f2306",
   "metadata": {},
   "source": [
    "## Chunk References: Smaller Child Chunks Referring to Bigger Parent Chunk\n",
    "\n",
    "In this usage example, we show how to build a graph of smaller chunks pointing to bigger parent chunks.\n",
    "\n",
    "During query-time, we retrieve smaller chunks, but we follow references to bigger chunks. This allows us to have more context for synthesis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49c784d8-71e6-42bc-84d9-a2aea4217b8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "sub_chunk_sizes = [128, 256, 512]\n",
    "sub_node_parsers = [SentenceSplitter(chunk_size=c) for c in sub_chunk_sizes]\n",
    "\n",
    "all_nodes = []\n",
    "\n",
    "for base_node in base_nodes:\n",
    "    for n in sub_node_parsers:\n",
    "        sub_nodes = n.get_nodes_from_documents([base_node])\n",
    "        sub_inodes = [\n",
    "            IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes\n",
    "        ]\n",
    "        all_nodes.extend(sub_inodes)\n",
    "\n",
    "    # also add original node to node\n",
    "    original_node = IndexNode.from_text_node(base_node, base_node.node_id)\n",
    "    all_nodes.append(original_node)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d614088-b122-40ad-811a-29cc0c2a295e",
   "metadata": {},
   "outputs": [],
   "source": [
    "all_nodes_dict = {n.node_id: n for n in all_nodes}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a44ef2d5-0342-4073-831f-f35dd6f04dc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_index_chunk = VectorStoreIndex(all_nodes, embed_model=embed_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c06af99f-02be-4055-a6ea-3071ffe8fc8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_retriever_chunk = vector_index_chunk.as_retriever(similarity_top_k=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4c7c5e43-45b5-42d6-afc5-cb81ed3cb211",
   "metadata": {},
   "outputs": [],
   "source": [
    "retriever_chunk = RecursiveRetriever(\n",
    "    \"vector\",\n",
    "    retriever_dict={\"vector\": vector_retriever_chunk},\n",
    "    node_dict=all_nodes_dict,\n",
    "    verbose=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e9f7bcb-5442-4d2d-a7eb-814b68ebb45c",
   "metadata": {},
   "outputs": [],
   "source": [
    "nodes = retriever_chunk.retrieve(\n",
    "    \"Can you tell me about the key concepts for safety finetuning\"\n",
    ")\n",
    "for node in nodes:\n",
    "    display_source_node(node, source_length=2000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "411f26ad-d13b-4858-938e-efcfa899e8cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "query_engine_chunk = RetrieverQueryEngine.from_args(retriever_chunk, llm=llm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4cd98366-0d5f-4d04-87cd-b811990b7485",
   "metadata": {},
   "outputs": [],
   "source": [
    "response = query_engine_chunk.query(\n",
    "    \"Can you tell me about the key concepts for safety finetuning\"\n",
    ")\n",
    "print(str(response))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3bcc7379-c077-40b7-ba4e-f47f80def0c7",
   "metadata": {},
   "source": [
    "## Metadata References: Summaries + Generated Questions referring to a bigger chunk\n",
    "\n",
    "In this usage example, we show how to define additional context that references the source node.\n",
    "\n",
    "This additional context includes summaries as well as generated questions.\n",
    "\n",
    "During query-time, we retrieve smaller chunks, but we follow references to bigger chunks. This allows us to have more context for synthesis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24e40c5e-4868-487f-aaf4-f333aa4bda66",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core.node_parser import SentenceSplitter\n",
    "from llama_index.core.schema import IndexNode\n",
    "from llama_index.core.extractors import (\n",
    "    SummaryExtractor,\n",
    "    QuestionsAnsweredExtractor,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c5d6f87-790e-4b82-abb2-cc6944678b00",
   "metadata": {},
   "outputs": [],
   "source": [
    "extractors = [\n",
    "    SummaryExtractor(summaries=[\"self\"], show_progress=True),\n",
    "    QuestionsAnsweredExtractor(questions=5, show_progress=True),\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e47c706c-940e-499d-b742-eaf09a230b0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# run metadata extractor across base nodes, get back dictionaries\n",
    "metadata_dicts = []\n",
    "for extractor in extractors:\n",
    "    metadata_dicts.extend(extractor.extract(base_nodes))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2873327d-420a-4778-a83b-6fdf7aa21bcd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# cache metadata dicts\n",
    "def save_metadata_dicts(path):\n",
    "    with open(path, \"w\") as fp:\n",
    "        for m in metadata_dicts:\n",
    "            fp.write(json.dumps(m) + \"\\n\")\n",
    "\n",
    "\n",
    "def load_metadata_dicts(path):\n",
    "    with open(path, \"r\") as fp:\n",
    "        metadata_dicts = [json.loads(l) for l in fp.readlines()]\n",
    "        return metadata_dicts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e318efb2-9afa-4414-b37f-71738d73d01d",
   "metadata": {},
   "outputs": [],
   "source": [
    "save_metadata_dicts(\"data/llama2_metadata_dicts.jsonl\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4edce99f-8a96-4539-95e7-62aeeabb2ce9",
   "metadata": {},
   "outputs": [],
   "source": [
    "metadata_dicts = load_metadata_dicts(\"data/llama2_metadata_dicts.jsonl\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f18d2109-5fcb-4fd5-b147-23897fed8787",
   "metadata": {},
   "outputs": [],
   "source": [
    "# all nodes consists of source nodes, along with metadata\n",
    "import copy\n",
    "\n",
    "all_nodes = copy.deepcopy(base_nodes)\n",
    "for idx, d in enumerate(metadata_dicts):\n",
    "    inode_q = IndexNode(\n",
    "        text=d[\"questions_this_excerpt_can_answer\"],\n",
    "        index_id=base_nodes[idx].node_id,\n",
    "    )\n",
    "    inode_s = IndexNode(\n",
    "        text=d[\"section_summary\"], index_id=base_nodes[idx].node_id\n",
    "    )\n",
    "    all_nodes.extend([inode_q, inode_s])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f90ada6-0969-40cc-a4ec-3579b4900cdd",
   "metadata": {},
   "outputs": [],
   "source": [
    "all_nodes_dict = {n.node_id: n for n in all_nodes}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22abc768-83d5-41d0-84f0-533899c76894",
   "metadata": {},
   "outputs": [],
   "source": [
    "## Load index into vector index\n",
    "from llama_index.core import VectorStoreIndex\n",
    "from llama_index.llms.openai import OpenAI\n",
    "\n",
    "llm = OpenAI(model=\"gpt-3.5-turbo\")\n",
    "\n",
    "vector_index_metadata = VectorStoreIndex(all_nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d53938a-1322-41b1-ad11-169b13b9805a",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_retriever_metadata = vector_index_metadata.as_retriever(\n",
    "    similarity_top_k=2\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37ae791f-c183-4ad4-9a3a-253288ded5a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "retriever_metadata = RecursiveRetriever(\n",
    "    \"vector\",\n",
    "    retriever_dict={\"vector\": vector_retriever_metadata},\n",
    "    node_dict=all_nodes_dict,\n",
    "    verbose=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3cd85685-19eb-44cc-ad27-1d163eaddad6",
   "metadata": {},
   "outputs": [],
   "source": [
    "nodes = retriever_metadata.retrieve(\n",
    "    \"Can you tell me about the key concepts for safety finetuning\"\n",
    ")\n",
    "for node in nodes:\n",
    "    display_source_node(node, source_length=2000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5285854a-69a6-4bc4-a2a5-1004cc790a63",
   "metadata": {},
   "outputs": [],
   "source": [
    "query_engine_metadata = RetrieverQueryEngine.from_args(\n",
    "    retriever_metadata, llm=llm\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e0ada5c-9a83-4517-bbb7-899d4415d68a",
   "metadata": {},
   "outputs": [],
   "source": [
    "response = query_engine_metadata.query(\n",
    "    \"Can you tell me about the key concepts for safety finetuning\"\n",
    ")\n",
    "print(str(response))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9973bdca-d179-47d6-bd96-2631b36e1d94",
   "metadata": {},
   "source": [
    "## Evaluation\n",
    "\n",
    "We evaluate how well our recursive retrieval + node reference methods work using [Braintrust](https://www.braintrustdata.com/). Braintrust is the enterprise-grade stack for building AI products. From evaluations, to prompt playground, to data management, we take uncertainty and tedium out of incorporating AI into your business.\n",
    "\n",
    "We evaluate both chunk references as well as metadata references. We use embedding similarity lookup to retrieve the reference nodes. We compare both methods against a baseline retriever where we fetch the raw nodes directly. In terms of metrics, we evaluate using both hit-rate and MRR.\n",
    "\n",
    "You can see example evaluation dashboards here for the:\n",
    "- [base retriever](https://www.braintrustdata.com/app/braintrustdata.com/p/llamaindex-recurisve-retrievers/baseRetriever)\n",
    "- [recursive metadata retreiver](https://www.braintrustdata.com/app/braintrustdata.com/p/llamaindex-recurisve-retrievers/recursiveMetadataRetriever)\n",
    "- [recursive chunk retriever](https://www.braintrustdata.com/app/braintrustdata.com/p/llamaindex-recurisve-retrievers/recursiveChunkRetriever)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b3a30b7-2eb2-4eae-b0b9-1d4ec26ac915",
   "metadata": {},
   "source": [
    "### Dataset Generation\n",
    "\n",
    "We first generate a dataset of questions from the set of text chunks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3fe8ae8a-a2b2-4515-bcff-1145e14ede3d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core.evaluation import (\n",
    "    generate_question_context_pairs,\n",
    "    EmbeddingQAFinetuneDataset,\n",
    ")\n",
    "import nest_asyncio\n",
    "\n",
    "nest_asyncio.apply()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eef1b43d-996b-4b0a-becb-1cec08d9f8c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval_dataset = generate_question_context_pairs(base_nodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd3e2507-9157-48a5-909b-18eeb9ec01d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval_dataset.save_json(\"data/llama2_eval_dataset.json\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "611f07af-2006-4158-8dc6-59d11a269c8d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# optional\n",
    "eval_dataset = EmbeddingQAFinetuneDataset.from_json(\n",
    "    \"data/llama2_eval_dataset.json\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb4782a6-f3da-453f-93be-7683ed15b508",
   "metadata": {},
   "source": [
    "### Compare Results\n",
    "\n",
    "We run evaluations on each of the retrievers to measure hit rate and MRR.\n",
    "\n",
    "We find that retrievers with node references (either chunk or metadata) tend to perform better than retrieving the raw chunks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87798866-11bc-4f7f-b8aa-0a023309492f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "# set vector retriever similarity top k to higher\n",
    "top_k = 10\n",
    "\n",
    "\n",
    "def display_results(names, results_arr):\n",
    "    \"\"\"Display results from evaluate.\"\"\"\n",
    "\n",
    "    hit_rates = []\n",
    "    mrrs = []\n",
    "    for name, eval_results in zip(names, results_arr):\n",
    "        metric_dicts = []\n",
    "        for eval_result in eval_results:\n",
    "            metric_dict = eval_result.metric_vals_dict\n",
    "            metric_dicts.append(metric_dict)\n",
    "        results_df = pd.DataFrame(metric_dicts)\n",
    "\n",
    "        hit_rate = results_df[\"hit_rate\"].mean()\n",
    "        mrr = results_df[\"mrr\"].mean()\n",
    "        hit_rates.append(hit_rate)\n",
    "        mrrs.append(mrr)\n",
    "\n",
    "    final_df = pd.DataFrame(\n",
    "        {\"retrievers\": names, \"hit_rate\": hit_rates, \"mrr\": mrrs}\n",
    "    )\n",
    "    display(final_df)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35a83d70",
   "metadata": {},
   "source": [
    "Let's define some scoring functions and define our dataset data variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a52e6b48",
   "metadata": {},
   "outputs": [],
   "source": [
    "queries = eval_dataset.queries\n",
    "relevant_docs = eval_dataset.relevant_docs\n",
    "data = [\n",
    "    ({\"input\": queries[query], \"expected\": relevant_docs[query]})\n",
    "    for query in queries.keys()\n",
    "]\n",
    "\n",
    "\n",
    "def hitRateScorer(input, expected, output=None):\n",
    "    is_hit = any([id in expected for id in output])\n",
    "    return 1 if is_hit else 0\n",
    "\n",
    "\n",
    "def mrrScorer(input, expected, output=None):\n",
    "    for i, id in enumerate(output):\n",
    "        if id in expected:\n",
    "            return 1 / (i + 1)\n",
    "    return 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6d142c6-0374-43ec-af31-e02d246bd815",
   "metadata": {},
   "outputs": [],
   "source": [
    "import braintrust\n",
    "\n",
    "# Evaluate the chunk retriever\n",
    "vector_retriever_chunk = vector_index_chunk.as_retriever(similarity_top_k=10)\n",
    "retriever_chunk = RecursiveRetriever(\n",
    "    \"vector\",\n",
    "    retriever_dict={\"vector\": vector_retriever_chunk},\n",
    "    node_dict=all_nodes_dict,\n",
    "    verbose=False,\n",
    ")\n",
    "\n",
    "\n",
    "def runChunkRetriever(input, hooks):\n",
    "    retrieved_nodes = retriever_chunk.retrieve(input)\n",
    "    retrieved_ids = [node.node.node_id for node in retrieved_nodes]\n",
    "    return retrieved_ids\n",
    "\n",
    "\n",
    "chunkEval = await braintrust.Eval(\n",
    "    name=\"llamaindex-recurisve-retrievers\",\n",
    "    data=data,\n",
    "    task=runChunkRetriever,\n",
    "    scores=[hitRateScorer, mrrScorer],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae448fe7-3a66-45a6-8e8e-6ed3950e61b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the metadata retriever\n",
    "\n",
    "vector_retriever_metadata = vector_index_metadata.as_retriever(\n",
    "    similarity_top_k=10\n",
    ")\n",
    "retriever_metadata = RecursiveRetriever(\n",
    "    \"vector\",\n",
    "    retriever_dict={\"vector\": vector_retriever_metadata},\n",
    "    node_dict=all_nodes_dict,\n",
    "    verbose=False,\n",
    ")\n",
    "\n",
    "\n",
    "def runMetaDataRetriever(input, hooks):\n",
    "    retrieved_nodes = retriever_metadata.retrieve(input)\n",
    "    retrieved_ids = [node.node.node_id for node in retrieved_nodes]\n",
    "    return retrieved_ids\n",
    "\n",
    "\n",
    "metadataEval = await braintrust.Eval(\n",
    "    name=\"llamaindex-recurisve-retrievers\",\n",
    "    data=data,\n",
    "    task=runMetaDataRetriever,\n",
    "    scores=[hitRateScorer, mrrScorer],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d3fc029-7ccc-4ec4-b391-b7b86744b5d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the base retriever\n",
    "base_retriever = base_index.as_retriever(similarity_top_k=10)\n",
    "\n",
    "\n",
    "def runBaseRetriever(input, hooks):\n",
    "    retrieved_nodes = base_retriever.retrieve(input)\n",
    "    retrieved_ids = [node.node.node_id for node in retrieved_nodes]\n",
    "    return retrieved_ids\n",
    "\n",
    "\n",
    "baseEval = await braintrust.Eval(\n",
    "    name=\"llamaindex-recurisve-retrievers\",\n",
    "    data=data,\n",
    "    task=runBaseRetriever,\n",
    "    scores=[hitRateScorer, mrrScorer],\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
